#include <string>
#include <thread>
#include <chrono>

#include "config.hpp"
#include "misc.h"
#include "modbus.hpp"
#include "modbus_setting.hpp"
#include "sys_var.hpp"

#if ENABLE_MODBUS == 1
////////////////////////////////////////////////////////////////////////////////////////////////
const int modbus :: WAITING     = 1;
const int modbus :: SENDED      = 2;
const int modbus :: OVERTIME    = 3;
const int modbus :: CANCELED    = 4;
const int modbus :: FINISH      = 5;
const int modbus :: READY       = 6;
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
modbus :: mission :: mission( const ModbusSetting::stItem & m )
{
	memset( cmd , 0 , 20 );

	if( m ){
		size_t len = m.cmd_len;
		m.CloneCmd( ( char *)cmd ,  len );
		cmd_len = (int)len;

		timer( m.Interval() );
		timer.SetOwner( [this]{
		if( trig != modbus :: CANCELED ){
			trig = modbus::READY;
			timer.Start( -1 , true );
		}
		});
		
		trig = modbus::WAITING;
		overtime_count = 0;
	}else{
		__show_line_1( "%s%s%s" , TRED , "Illegal mission description." , TNORMAL );
		throw ERR_ILLEGAL_MB_MISSION_DESC;
	}
}

modbus :: mission :: ~mission()
{
    	timer.Stop();
}

modbus :: mission :: mission( const mission& b )
{
	timer.Stop();
	memset( cmd , 0 , 20 );
	memcpy( cmd , b.cmd , b.cmd_len );

	cmd_len = b.cmd_len;
	trig = modbus :: WAITING;
	overtime_count = b.overtime_count;

	timer( b.timer() );
	timer.SetOwner( [this]{
		if( trig != modbus :: CANCELED ){
			trig = modbus::READY;
			timer.Start( -1 , true );
		}

		__show_line_1( "%s trig = %d %s" , TYELLOW , trig.load() , TNORMAL );
	});
}

modbus :: mission :: operator bool()
{
	if( cmd_len == 8 && cmd[ 1 ] > 0 ) return true;
	return false;
}

bool modbus :: mission :: operator ==( const mission& b )
{
    	return true;
}

modbus :: mission& 
modbus :: mission :: operator =( const mission& b )
{
	timer.Stop();
	memset( cmd , 0 , 20 );
	memcpy( cmd , b.cmd , b.cmd_len );
	cmd_len = b.cmd_len;
	overtime_count = b.overtime_count;
	trig = WAITING;

	timer( b.timer() );
	timer.SetOwner( [this]{
		// 如果计时时间到达后，并且任务状态不是取消状态则将状态设置为就绪状态。
		// 在就绪状态下的任务可以参与采集操作。
		if( trig != modbus :: CANCELED ){
			trig = modbus::READY;

			timer.Start( -1 , true);
		}
	});

	return *this;
}

bool modbus :: mission :: IsReady()
{
    	return trig == modbus :: READY;
}

bool modbus :: mission :: IsOverTime()
{
    	return trig == modbus :: OVERTIME;
}

bool modbus :: mission :: IsCancel( int maxCount )
{
    	return overtime_count >= maxCount;
}

bool modbus :: mission :: IsWaiting()
{
    	return trig == modbus::WAITING;
}

bool modbus :: mission :: IsFinish()
{
    return trig == modbus::FINISH;
}

modbus :: modbus(long overtime , long intval , io_type type , filterList * l )
:filterIO( type , l ),
m_com_overtime( [ this ]{       // 通讯超时计时器超时操作
      m_missions[ m_cur.load() ].trig = OVERTIME;
      m_missions[ m_cur.load() ].overtime_count ++;
} , overtime , true )
{
	m_cur = 0;
	long   cycle = 5000, ovt = 2000;
	std::shared_ptr< CSysVar> ptvar = GetOrCreateSysVar();
	m_overtime_max_count = 10;
	try{
		if( ptvar ){
			ptvar->GetValue( "/modbus/cycle_time" , cycle );
			ptvar->GetValue( "/modbus/overtime" , ovt );
			m_com_overtime( ovt );
			// 读取任务列表
			std::string    str;
			ptvar->GetValue( "/modbus/path" , str );
			ModbusSetting  doc( str );
			ModbusSetting::ModMission   mis = doc.Mission();

			for( int i =(int) mis.size() - 1; i >= 0 ; i -- ){
				mission item( mis[ i ] );
				m_missions.push_back( item );
			}
		}else{
			__show_line_1( "%s%s%s" , TRED , "System variable module initialize fail." , TNORMAL );
			abort();
		}
	}
	catch( ... ){
		__show_line_1( "%s%s%s%s%s" , TNORMAL , "\tInitialize Modbus Module..." , TRED , "[ FAIL ]" , TNORMAL );
		abort();
	}
	// 初始化轮询计时器。
	m_timer( cycle );
	// 采集任务由采集计时器触发，并在出发函数中执行任务。如果任务发送的时间比较长
	// 在这段时间内也是采用等待的方式。直到发送完成后再执行下次的启动操作。
	m_timer.SetOwner( [this]{  
		do_cycle();                         // 执行采集任务
		m_timer.Start( -1 , true );  
	});
	m_cur = 0;
	
	std::cout << TNORMAL << "\tInitialize MODBUS ... " << TGREEN  << "[ OK ]" << TNORMAL << std::endl;
	// 启动计时器开始MODBUS采集
	this->run();
}

modbus :: ~modbus()
{
	for( auto i = m_missions.begin(); i != m_missions.end(); i ++ ){ i -> timer.Stop(); }

	std::this_thread::sleep_for( std::chrono::milliseconds( 100 ));

	m_timer.Stop();
	m_com_overtime.Stop();
}

void modbus :: do_cycle()
{
	mission m = m_missions[ m_cur.load() ];

	size_t tmp_cur = m_cur.load();
	while( !m_missions[ m_cur.load() ].IsReady() ){
		m_cur ++;
		if( m_cur >= m_missions.size() ){ m_cur = 0; }

		if( m_cur == tmp_cur ) break;
	}

	if( m_missions[ m_cur.load() ].IsReady() ){// 如果当前项目状态就绪，则可以执行采集任务
		backward( ptList[ 0 ]->rbegin(), m.cmd_len , ( const char *)( m.cmd ) , OK );
		
		m_missions[ m_cur.load() ].timer.Start( -1 , true );
		m_missions[ m_cur.load() ].trig = FINISH;
	}
	/*{
		// 如果当前任务已经结束则继续执行下一个任务
		if( m_missions[ m_cur.load() ].IsFinish() ){
		m_missions[ m_cur.load() ].trig = READY;
		m_cur ++;
		}

		if( m_cur >= m_missions.size() ){ m_cur = 0;  }
		if( !m_missions[ m_cur.load() ] ) return;

		if( m_missions[ m_cur.load() ].IsReady() ){// 如果当前项目状态就绪，则可以执行采集任务
		backward( ptList[ 0 ]->rbegin(), m.cmd_len , ( const char *)( m.cmd ) , OK );
		m_com_overtime.Start( -1 , true );
		m_missions[ m_cur.load() ].timer.Start( -1 , true );
		}else if( m_missions[ m_cur.load() ].IsWaiting() ){// 如果当前任务是在等待期，则跳过这个文物
		m_cur ++;
		m_com_overtime.Stop();
		}else if( m_missions[ m_cur.load() ].IsOverTime() ){ // 如果当前任务通讯超时并且没有超过最大尝试次数，则尝试重新发送采集指令；
		// 如果已经超过了尝试次数，则放弃这个任务将状态设置为取消。
		m_com_overtime.Stop();
		backward( ptList[ 0 ]->rbegin(), m.cmd_len , ( const char *)( m.cmd ) , OK );
		
		m_missions[ m_cur.load() ].trig = SENDED;
		m_com_overtime.Start( -1 , true );
		}
	}
	*/
}

bool modbus :: on_forward( size_t len , const char * data , err_code &err , size_t& rlen , char ** rd )
{
	// 如果串口接收到的数据有效，则任务当前任务运行结束
	/// @note 如果再模块进行MODBUS任务采集的过程中还包含了其他串口操作的数据，这个判断
	/// 会不准确。因此实际在应用的过程中应该注意使用modbus采集的时候尽量不要外接其他串口
	/// 操作。 
	//if( data != nullptr && len != 0 ){
	//   m_missions[ m_cur.load() ].trig = FINISH;
	//}

	*rd = ( char *)data;
	rlen = len;
	return false;
}

bool modbus :: on_backward( size_t len , const char * data , err_code &err , size_t& rlen , char ** rd  )
{
	*rd = ( char *)data;
	rlen = len;
	return true;
}

#endif